source: github/program/steps/mail/func.inc @ 197601e

HEADcourier-fixdev-browser-capabilitiespdorelease-0.6release-0.7release-0.8
Last change on this file since 197601e was 197601e, checked in by thomascube <thomas@…>, 5 years ago

Next step: introduce the application class 'rcmail' and get rid of some global vars

  • 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/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// define url for getting message parts
58if (strlen($_GET['_uid']))
59  $GET_URL = rcmail_url('get', array('_mbox'=>$IMAP->get_mailbox_name(), '_uid'=>get_input_value('_uid', RCUBE_INPUT_GET)));
60
61
62// set current mailbox in client environment
63$OUTPUT->set_env('mailbox', $IMAP->get_mailbox_name());
64$OUTPUT->set_env('quota', $IMAP->get_capability('quota'));
65
66if ($CONFIG['trash_mbox'])
67  $OUTPUT->set_env('trash_mailbox', $CONFIG['trash_mbox']);
68if ($CONFIG['drafts_mbox'])
69  $OUTPUT->set_env('drafts_mailbox', $CONFIG['drafts_mbox']);
70if ($CONFIG['junk_mbox'])
71  $OUTPUT->set_env('junk_mailbox', $CONFIG['junk_mbox']);
72
73if (!$OUTPUT->ajax_call)
74  rcube_add_label('checkingmail', 'deletemessage', 'movemessagetotrash', 'movingmessage');
75
76// set page title
77if (empty($RCMAIL->action) || $RCMAIL->action == 'list')
78  $OUTPUT->set_pagetitle(rcmail_localize_foldername($IMAP->get_mailbox_name()));
79
80
81
82// return the message list as HTML table
83function rcmail_message_list($attrib)
84  {
85  global $IMAP, $CONFIG, $COMM_PATH, $OUTPUT;
86
87  $skin_path = $CONFIG['skin_path'];
88  $image_tag = '<img src="%s%s" alt="%s" border="0" />';
89
90  // check to see if we have some settings for sorting
91  $sort_col   = $_SESSION['sort_col'];
92  $sort_order = $_SESSION['sort_order'];
93 
94  // add some labels to client
95  rcube_add_label('from', 'to');
96
97  // get message headers
98  $a_headers = $IMAP->list_headers('', '', $sort_col, $sort_order);
99
100  // add id to message list table if not specified
101  if (!strlen($attrib['id']))
102    $attrib['id'] = 'rcubemessagelist';
103
104  // allow the following attributes to be added to the <table> tag
105  $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
106
107  $out = '<table' . $attrib_str . ">\n";
108
109
110  // define list of cols to be displayed
111  $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
112  $a_sort_cols = array('subject', 'date', 'from', 'to', 'size');
113
114  $mbox = $IMAP->get_mailbox_name();
115 
116  // show 'to' instead of from in sent messages
117  if (($mbox==$CONFIG['sent_mbox'] || $mbox==$CONFIG['drafts_mbox']) && ($f = array_search('from', $a_show_cols))
118      && !array_search('to', $a_show_cols))
119    $a_show_cols[$f] = 'to';
120 
121  // add col definition
122  $out .= '<colgroup>';
123  $out .= '<col class="icon" />';
124
125  foreach ($a_show_cols as $col)
126    $out .= sprintf('<col class="%s" />', $col);
127
128  $out .= '<col class="icon" />';
129  $out .= "</colgroup>\n";
130
131  // add table title
132  $out .= "<thead><tr>\n<td class=\"icon\">&nbsp;</td>\n";
133
134  $javascript = '';
135  foreach ($a_show_cols as $col)
136    {
137    // get column name
138    $col_name = Q(rcube_label($col));
139
140    // make sort links
141    $sort = '';
142    if ($IMAP->get_capability('sort') && in_array($col, $a_sort_cols))
143      {
144      // have buttons configured
145      if (!empty($attrib['sortdescbutton']) || !empty($attrib['sortascbutton']))
146        {
147        $sort = '&nbsp;&nbsp;';
148
149        // asc link
150        if (!empty($attrib['sortascbutton']))
151          {
152          $sort .= $OUTPUT->button(array(
153            'command' => 'sort',
154            'prop' => $col.'_ASC',
155            'image' => $attrib['sortascbutton'],
156            'align' => 'absmiddle',
157            'title' => 'sortasc'));
158          }       
159       
160        // desc link
161        if (!empty($attrib['sortdescbutton']))
162          {
163          $sort .= $OUTPUT->button(array(
164            'command' => 'sort',
165            'prop' => $col.'_DESC',
166            'image' => $attrib['sortdescbutton'],
167            'align' => 'absmiddle',
168            'title' => 'sortdesc'));
169          }
170        }
171      // just add a link tag to the header
172      else
173        {
174        $col_name = sprintf(
175          '<a href="./#sort" onclick="return %s.command(\'sort\',\'%s\',this)" title="%s">%s</a>',
176          JS_OBJECT_NAME,
177          $col,
178          rcube_label('sortby'),
179          $col_name);
180        }
181      }
182     
183    $sort_class = $col==$sort_col ? " sorted$sort_order" : '';
184
185    // put it all together
186    $out .= '<td class="'.$col.$sort_class.'" id="rcmHead'.$col.'">' . "$col_name$sort</td>\n";   
187    }
188
189  $out .= '<td class="icon">'.($attrib['attachmenticon'] ? sprintf($image_tag, $skin_path, $attrib['attachmenticon'], '') : '')."</td>\n";
190  $out .= "</tr></thead>\n<tbody>\n";
191
192  // no messages in this mailbox
193  if (!sizeof($a_headers))
194    $OUTPUT->show_message('nomessagesfound', 'notice');
195
196
197  $a_js_message_arr = array();
198
199  // create row for each message
200  foreach ($a_headers as $i => $header)  //while (list($i, $header) = each($a_headers))
201    {
202    $message_icon = $attach_icon = '';
203    $js_row_arr = array();
204    $zebra_class = $i%2 ? 'even' : 'odd';
205
206    // set messag attributes to javascript array
207    if ($header->deleted)
208      $js_row_arr['deleted'] = true;
209    if (!$header->seen)
210      $js_row_arr['unread'] = true;
211    if ($header->answered)
212      $js_row_arr['replied'] = true;
213    // set message icon 
214    if ($attrib['deletedicon'] && $header->deleted)
215      $message_icon = $attrib['deletedicon'];
216    else if ($attrib['unreadicon'] && !$header->seen)
217      $message_icon = $attrib['unreadicon'];
218    else if ($attrib['repliedicon'] && $header->answered)
219      $message_icon = $attrib['repliedicon'];
220    else if ($attrib['messageicon'])
221      $message_icon = $attrib['messageicon'];
222   
223    // set attachment icon
224    if ($attrib['attachmenticon'] && preg_match("/multipart\/[mr]/i", $header->ctype))
225      $attach_icon = $attrib['attachmenticon'];
226       
227    $out .= sprintf('<tr id="rcmrow%d" class="message%s%s %s">'."\n",
228                    $header->uid,
229                    $header->seen ? '' : ' unread',
230                    $header->deleted ? ' deleted' : '',
231                    $zebra_class);   
232   
233    $out .= sprintf("<td class=\"icon\">%s</td>\n", $message_icon ? sprintf($image_tag, $skin_path, $message_icon, '') : '');
234   
235    // format each col
236    foreach ($a_show_cols as $col)
237      {
238      if ($col=='from' || $col=='to')
239        $cont = Q(rcmail_address_string($header->$col, 3, $attrib['addicon']), 'show');
240      else if ($col=='subject')
241        {
242        $action = $mbox==$CONFIG['drafts_mbox'] ? 'compose' : 'show';
243        $uid_param = $mbox==$CONFIG['drafts_mbox'] ? '_draf_uid' : '_uid';
244        $cont = Q(rcube_imap::decode_mime_string($header->$col, $header->charset));
245        if (empty($cont)) $cont = Q(rcube_label('nosubject'));
246        $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);
247        }
248      else if ($col=='size')
249        $cont = show_bytes($header->$col);
250      else if ($col=='date')
251        $cont = format_date($header->date);
252      else
253        $cont = Q($header->$col);
254       
255      $out .= '<td class="'.$col.'">' . $cont . "</td>\n";
256      }
257
258    $out .= sprintf("<td class=\"icon\">%s</td>\n", $attach_icon ? sprintf($image_tag, $skin_path, $attach_icon, '') : '');
259    $out .= "</tr>\n";
260   
261    if (sizeof($js_row_arr))
262      $a_js_message_arr[$header->uid] = $js_row_arr;
263    }
264 
265  // complete message table
266  $out .= "</tbody></table>\n";
267 
268 
269  $message_count = $IMAP->messagecount();
270 
271  // set client env
272  $OUTPUT->add_gui_object('mailcontframe', 'mailcontframe');
273  $OUTPUT->add_gui_object('messagelist', $attrib['id']);
274  $OUTPUT->set_env('messagecount', $message_count);
275  $OUTPUT->set_env('current_page', $IMAP->list_page);
276  $OUTPUT->set_env('pagecount', ceil($message_count/$IMAP->page_size));
277  $OUTPUT->set_env('sort_col', $sort_col);
278  $OUTPUT->set_env('sort_order', $sort_order);
279 
280  if ($attrib['messageicon'])
281    $OUTPUT->set_env('messageicon', $skin_path . $attrib['messageicon']);
282  if ($attrib['deletedicon'])
283    $OUTPUT->set_env('deletedicon', $skin_path . $attrib['deletedicon']);
284  if ($attrib['unreadicon'])
285    $OUTPUT->set_env('unreadicon', $skin_path . $attrib['unreadicon']);
286  if ($attrib['repliedicon'])
287    $OUTPUT->set_env('repliedicon', $skin_path . $attrib['repliedicon']);
288  if ($attrib['attachmenticon'])
289    $OUTPUT->set_env('attachmenticon', $skin_path . $attrib['attachmenticon']);
290 
291  $OUTPUT->set_env('messages', $a_js_message_arr);
292  $OUTPUT->set_env('coltypes', $a_show_cols);
293 
294  $OUTPUT->include_script('list.js');
295 
296  return $out;
297  }
298
299
300// return javascript commands to add rows to the message list
301function rcmail_js_message_list($a_headers, $insert_top=FALSE)
302  {
303  global $CONFIG, $IMAP, $OUTPUT;
304
305  $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject');
306  $mbox = $IMAP->get_mailbox_name();
307
308  // show 'to' instead of from in sent messages
309  if (($mbox == $CONFIG['sent_mbox'] || $mbox == $CONFIG['drafts_mbox'])
310      && (($f = array_search('from', $a_show_cols)) !== false) && array_search('to', $a_show_cols) === false)
311    $a_show_cols[$f] = 'to';
312
313  $OUTPUT->command('set_message_coltypes', $a_show_cols);
314
315  // loop through message headers
316  foreach ($a_headers as $n => $header)
317    {
318    $a_msg_cols = array();
319    $a_msg_flags = array();
320   
321    if (empty($header))
322      continue;
323
324    // format each col; similar as in rcmail_message_list()
325    foreach ($a_show_cols as $col)
326      {
327      if ($col=='from' || $col=='to')
328        $cont = Q(rcmail_address_string($header->$col, 3), 'show');
329      else if ($col=='subject')
330        {
331        $action = $mbox==$CONFIG['drafts_mbox'] ? 'compose' : 'show';
332        $uid_param = $mbox==$CONFIG['drafts_mbox'] ? '_draf_uid' : '_uid';
333        $cont = Q(rcube_imap::decode_mime_string($header->$col, $header->charset));
334        if (!$cont) $cont = Q(rcube_label('nosubject'));
335        $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);
336        }
337      else if ($col=='size')
338        $cont = show_bytes($header->$col);
339      else if ($col=='date')
340        $cont = format_date($header->date);
341      else
342        $cont = Q($header->$col);
343         
344      $a_msg_cols[$col] = $cont;
345      }
346
347    $a_msg_flags['deleted'] = $header->deleted ? 1 : 0;
348    $a_msg_flags['unread'] = $header->seen ? 0 : 1;
349    $a_msg_flags['replied'] = $header->answered ? 1 : 0;
350    $OUTPUT->command('add_message_row',
351      $header->uid,
352      $a_msg_cols,
353      $a_msg_flags,
354      preg_match("/multipart\/m/i", $header->ctype),
355      $insert_top);
356    }
357  }
358
359
360// return an HTML iframe for loading mail content
361function rcmail_messagecontent_frame($attrib)
362  {
363  global $OUTPUT;
364 
365  if (empty($attrib['id']))
366    $attrib['id'] = 'rcmailcontentwindow';
367
368  // allow the following attributes to be added to the <iframe> tag
369  $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'src', 'width', 'height', 'frameborder'));
370  $framename = $attrib['id'];
371
372  $out = sprintf('<iframe name="%s"%s></iframe>'."\n",
373         $framename,
374         $attrib_str);
375
376  $OUTPUT->set_env('contentframe', $framename);
377  $OUTPUT->set_env('blankpage', $attrib['src'] ? $OUTPUT->abs_url($attrib['src']) : 'program/blank.gif');
378
379  return $out;
380  }
381
382
383function rcmail_messagecount_display($attrib)
384  {
385  global $IMAP, $OUTPUT;
386 
387  if (!$attrib['id'])
388    $attrib['id'] = 'rcmcountdisplay';
389
390  $OUTPUT->add_gui_object('countdisplay', $attrib['id']);
391
392  // allow the following attributes to be added to the <span> tag
393  $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
394
395 
396  $out = '<span' . $attrib_str . '>';
397  $out .= rcmail_get_messagecount_text();
398  $out .= '</span>';
399  return $out;
400  }
401
402
403function rcmail_quota_display($attrib)
404  {
405  global $OUTPUT, $COMM_PATH;
406
407  if (!$attrib['id'])
408    $attrib['id'] = 'rcmquotadisplay';
409
410  if(isset($attrib['display']))
411    $_SESSION['quota_display'] = $attrib['display'];
412
413  $OUTPUT->add_gui_object('quotadisplay', $attrib['id']);
414
415  // allow the following attributes to be added to the <span> tag
416  $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'display'));
417
418  $out = '<span' . $attrib_str . '>';
419  $out .= rcmail_quota_content();
420  $out .= '</span>';
421  return $out;
422  }
423
424
425function rcmail_quota_content($quota=NULL)
426  {
427  global $IMAP, $COMM_PATH;
428
429  $display = isset($_SESSION['quota_display']) ? $_SESSION['quota_display'] : '';
430
431  if (is_array($quota) && !empty($quota['used']) && !empty($quota['total']))
432    {
433      if (!isset($quota['percent']))
434        $quota['percent'] = $quota['used'] / $quota['total'];
435    }
436  elseif (!$IMAP->get_capability('QUOTA'))
437    return rcube_label('unknown');
438  else
439    $quota = $IMAP->get_quota();
440
441  if ($quota)
442    {
443    $quota_text = sprintf('%s / %s (%.0f%%)',
444                          show_bytes($quota['used'] * 1024),
445                          show_bytes($quota['total'] * 1024),
446                          $quota['percent']);
447
448    // show quota as image (by Brett Patterson)
449    if ($display == 'image' && function_exists('imagegif'))
450      {
451      $attrib = array('width' => 100, 'height' => 14);
452      $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" />',
453                            $quota['used'], $quota['total'],
454                            $attrib['width'], $attrib['height'],
455                            $attrib['width'], $attrib['height'],
456                            $quota_text,
457                            show_bytes($quota["used"] * 1024),
458                            show_bytes($quota["total"] * 1024));
459      }
460    }
461  else
462    $quota_text = rcube_label('unlimited');
463
464  return $quota_text;
465  }
466
467
468function rcmail_get_messagecount_text($count=NULL, $page=NULL)
469  {
470  global $IMAP, $MESSAGE;
471 
472  if (isset($MESSAGE['index']))
473    {
474    return rcube_label(array('name' => 'messagenrof',
475                             'vars' => array('nr'  => $MESSAGE['index']+1,
476                                             'count' => $count!==NULL ? $count : $IMAP->messagecount())));
477    }
478
479  if ($page===NULL)
480    $page = $IMAP->list_page;
481   
482  $start_msg = ($page-1) * $IMAP->page_size + 1;
483  $max = $count!==NULL ? $count : $IMAP->messagecount();
484
485  if ($max==0)
486    $out = rcube_label('mailboxempty');
487  else
488    $out = rcube_label(array('name' => 'messagesfromto',
489                              'vars' => array('from'  => $start_msg,
490                                              'to'    => min($max, $start_msg + $IMAP->page_size - 1),
491                                              'count' => $max)));
492
493  return Q($out);
494  }
495
496
497/* Stolen from Squirrelmail */
498function sq_deent(&$attvalue, $regex, $hex=false)
499  {
500  $ret_match = false;
501  preg_match_all($regex, $attvalue, $matches);
502  if (is_array($matches) && sizeof($matches[0]) > 0)
503    {
504    $repl = Array();
505    for ($i = 0; $i < sizeof($matches[0]); $i++)
506      {
507      $numval = $matches[1][$i];
508      if ($hex)
509        $numval = hexdec($numval);
510      $repl{$matches[0][$i]} = chr($numval);
511      }
512    $attvalue = strtr($attvalue, $repl);
513    return true;
514    }
515  else
516    return false;
517  }
518
519
520/* Stolen verbatim from Squirrelmail */
521function sq_defang(&$attvalue)
522  {
523  /* Skip this if there aren't ampersands or backslashes. */
524  if ((strpos($attvalue, '&') === false) &&
525      (strpos($attvalue, '\\') === false))
526    return;
527  $m = false;
528  do
529    {
530    $m = false;
531    $m = $m || sq_deent($attvalue, '/\&#0*(\d+);*/s');
532    $m = $m || sq_deent($attvalue, '/\&#x0*((\d|[a-f])+);*/si', true);
533    $m = $m || sq_deent($attvalue, '/\\\\(\d+)/s', true);
534    } while ($m == true);
535  $attvalue = stripslashes($attvalue);
536  }
537
538
539function rcmail_html_filter($html)
540  {
541  preg_match_all('/<\/?\w+((\s+\w+(\s*=\s*(?:".*?"|\'.*?\'|[^\'">\s]+))?)+\s*|\s*)\/?>/', $html, $tags);
542
543  /* From Squirrelmail: Translate all dangerous Unicode or Shift_JIS characters which are accepted by
544   * IE as regular characters. */
545  $replace = array(array('&#x029F;', '&#0671;',  /* L UNICODE IPA Extension */
546                         '&#x0280;', '&#0640;',  /* R UNICODE IPA Extension */
547                         '&#x0274;', '&#0628;',  /* N UNICODE IPA Extension */
548                         '&#xFF25;', '&#65317;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER E */
549                         '&#xFF45;', '&#65349;', /* Unicode FULLWIDTH LATIN SMALL LETTER E */
550                         '&#xFF38;', '&#65336;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER X */
551                         '&#xFF58;', '&#65368;', /* Unicode FULLWIDTH LATIN SMALL LETTER X */
552                         '&#xFF30;', '&#65328;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER P */
553                         '&#xFF50;', '&#65360;', /* Unicode FULLWIDTH LATIN SMALL LETTER P */
554                         '&#xFF32;', '&#65330;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER R */
555                         '&#xFF52;', '&#65362;', /* Unicode FULLWIDTH LATIN SMALL LETTER R */
556                         '&#xFF33;', '&#65331;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER S */
557                         '&#xFF53;', '&#65363;', /* Unicode FULLWIDTH LATIN SMALL LETTER S */
558                         '&#xFF29;', '&#65321;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER I */
559                         '&#xFF49;', '&#65353;', /* Unicode FULLWIDTH LATIN SMALL LETTER I */
560                         '&#xFF2F;', '&#65327;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER O */
561                         '&#xFF4F;', '&#65359;', /* Unicode FULLWIDTH LATIN SMALL LETTER O */
562                         '&#xFF2E;', '&#65326;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER N */
563                         '&#xFF4E;', '&#65358;', /* Unicode FULLWIDTH LATIN SMALL LETTER N */
564                         '&#xFF2C;', '&#65324;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER L */
565                         '&#xFF4C;', '&#65356;', /* Unicode FULLWIDTH LATIN SMALL LETTER L */
566                         '&#xFF35;', '&#65333;', /* Unicode FULLWIDTH LATIN CAPITAL LETTER U */
567                         '&#xFF55;', '&#65365;', /* Unicode FULLWIDTH LATIN SMALL LETTER U */
568                         '&#x207F;', '&#8319;' , /* Unicode SUPERSCRIPT LATIN SMALL LETTER N */
569                         "\xEF\xBC\xA5", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER E */
570                                         /* in unicode this is some Chinese char range */
571                         "\xEF\xBD\x85", /* Shift JIS FULLWIDTH LATIN SMALL LETTER E */
572                         "\xEF\xBC\xB8", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER X */
573                         "\xEF\xBD\x98", /* Shift JIS FULLWIDTH LATIN SMALL LETTER X */
574                         "\xEF\xBC\xB0", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER P */
575                         "\xEF\xBD\x90", /* Shift JIS FULLWIDTH LATIN SMALL LETTER P */
576                         "\xEF\xBC\xB2", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER R */
577                         "\xEF\xBD\x92", /* Shift JIS FULLWIDTH LATIN SMALL LETTER R */
578                         "\xEF\xBC\xB3", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER S */
579                         "\xEF\xBD\x93", /* Shift JIS FULLWIDTH LATIN SMALL LETTER S */
580                         "\xEF\xBC\xA9", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER I */
581                         "\xEF\xBD\x89", /* Shift JIS FULLWIDTH LATIN SMALL LETTER I */
582                         "\xEF\xBC\xAF", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER O */
583                         "\xEF\xBD\x8F", /* Shift JIS FULLWIDTH LATIN SMALL LETTER O */
584                         "\xEF\xBC\xAE", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER N */
585                         "\xEF\xBD\x8E", /* Shift JIS FULLWIDTH LATIN SMALL LETTER N */
586                         "\xEF\xBC\xAC", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER L */
587                         "\xEF\xBD\x8C", /* Shift JIS FULLWIDTH LATIN SMALL LETTER L */
588                         "\xEF\xBC\xB5", /* Shift JIS FULLWIDTH LATIN CAPITAL LETTER U */
589                         "\xEF\xBD\x95", /* Shift JIS FULLWIDTH LATIN SMALL LETTER U */
590                         "\xE2\x81\xBF", /* Shift JIS FULLWIDTH SUPERSCRIPT N */
591                         "\xCA\x9F",   /* L UNICODE IPA Extension */
592                         "\xCA\x80",   /* R UNICODE IPA Extension */
593                         "\xC9\xB4"),  /* N UNICODE IPA Extension */
594                   array('l', 'l', 'r', 'r', 'n', 'n', 'E', 'E', 'e', 'e', 'X', 'X', 'x', 'x',
595                         'P', 'P', 'p', 'p', 'R', 'R', 'r', 'r', 'S', 'S', 's', 's', 'I', 'I',
596                         'i', 'i', 'O', 'O', 'o', 'o', 'N', 'N', 'n', 'n', 'L', 'L', 'l', 'l',
597                         'U', 'U', 'u', 'u', 'n', 'n', 'E', 'e', 'X', 'x', 'P', 'p', 'R', 'r',
598                         'S', 's', 'I', 'i', 'O', 'o', 'N', 'n', 'L', 'l', 'U', 'u', 'n', 'l', 'r', 'n'));
599  if ((count($tags)>3) && (count($tags[3])>0))
600    foreach ($tags[3] as $nr=>$value)
601      {
602      /* Remove comments */
603      $newvalue = preg_replace('/(\/\*.*\*\/)/','$2',$value);
604      /* Translate dangerous characters */
605      $newvalue = str_replace($replace[0], $replace[1], $newvalue);
606      sq_defang($newvalue);
607      /* Rename dangerous CSS */
608      $newvalue = preg_replace('/expression/i', 'idiocy', $newvalue);
609      $newvalue = preg_replace('/url/i', 'idiocy', $newvalue);
610      $newattrs = preg_replace('/'.preg_quote($value, '/').'$/', $newvalue, $tags[1][$nr]);
611      $newtag = preg_replace('/'.preg_quote($tags[1][$nr], '/').'/', $newattrs, $tags[0][$nr]);
612      $html = preg_replace('/'.preg_quote($tags[0][$nr], '/').'/', $newtag, $html);
613      }
614  return $html;
615  }
616
617
618function rcmail_print_body($part, $safe=FALSE, $plain=FALSE)
619  {
620  global $IMAP, $REMOTE_OBJECTS;
621 
622  $body = is_array($part->replaces) ? strtr($part->body, $part->replaces) : $part->body;
623
624  // convert html to text/plain
625  if ($part->ctype_secondary=='html' && $plain)
626    {
627    $txt = new html2text($body, false, true);
628    $body = $txt->get_text();
629    $part->ctype_secondary = 'plain';
630    }
631   
632  // text/html
633  if ($part->ctype_secondary=='html')
634    {
635    // remove charset specification in HTML message
636    $body = preg_replace('/charset=[a-z0-9\-]+/i', '', $body);
637
638    if (!$safe)  // remove remote images and scripts
639      {
640      $remote_patterns = array('/<img\s+(.*)src=(["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)(\2|\s|>)/Ui',
641                               '/(src|background)=(["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)(\2|\s|>)/Ui',
642                               '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i',
643                               '/(<link.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i',
644                               '/url\s*\(["\']?([hftps]{3,5}:\/{2}[^"\'\s]+)["\']?\)/i',
645                               '/url\s*\(["\']?([\.\/]+[^"\'\s]+)["\']?\)/i',
646                               '/<script.+<\/script>/Umis');
647
648      $remote_replaces = array('<img \\1src=\\2./program/blocked.gif\\4',
649                               '',
650                               '',
651                               '',
652                               'none',
653                               'none',
654                               '');
655     
656      // set flag if message containes remote obejcts that where blocked
657      foreach ($remote_patterns as $pattern)
658        {
659        if (preg_match($pattern, $body))
660          {
661          $REMOTE_OBJECTS = TRUE;
662          break;
663          }
664        }
665
666      $body = preg_replace($remote_patterns, $remote_replaces, $body);
667      }
668
669    return Q(rcmail_html_filter($body), 'show', FALSE);
670    }
671
672  // text/enriched
673  if ($part->ctype_secondary=='enriched')
674    {
675    return Q(enriched_to_html($body), 'show');
676    }
677  else
678    {
679    // make links and email-addresses clickable
680    $convert_patterns = $convert_replaces = $replace_strings = array();
681   
682    $url_chars = 'a-z0-9_\-\+\*\$\/&%=@#:;';
683    $url_chars_within = '\?\.~,!';
684
685    $convert_patterns[] = "/([\w]+):\/\/([a-z0-9\-\.]+[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
686    $convert_replaces[] = "rcmail_str_replacement('<a href=\"\\1://\\2\" target=\"_blank\">\\1://\\2</a>', \$replace_strings)";
687
688    $convert_patterns[] = "/([^\/:]|\s)(www\.)([a-z0-9\-]{2,}[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie";
689    $convert_replaces[] = "rcmail_str_replacement('\\1<a href=\"http://\\2\\3\" target=\"_blank\">\\2\\3</a>', \$replace_strings)";
690   
691    $convert_patterns[] = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/ie';
692    $convert_replaces[] = "rcmail_str_replacement('<a href=\"mailto:\\1\" onclick=\"return ".JS_OBJECT_NAME.".command(\'compose\',\'\\1\',this)\">\\1</a>', \$replace_strings)";
693   
694//    if ($part->ctype_parameters['format'] != 'flowed')
695//      $body = wordwrap(trim($body), 80);
696
697    $body = preg_replace($convert_patterns, $convert_replaces, $body);
698
699    // split body into single lines
700    $a_lines = preg_split('/\r?\n/', $body);
701    $quote_level = 0;
702
703    // colorize quoted parts
704    for($n=0; $n<sizeof($a_lines); $n++)
705      {
706      $line = $a_lines[$n];
707      $quotation = '';
708      $q = 0;
709     
710      if (preg_match('/^(>+\s*)+/', $line, $regs))
711        {
712        $q    = strlen(preg_replace('/\s/', '', $regs[0]));
713        $line = substr($line, strlen($regs[0]));
714
715        if ($q > $quote_level)
716          $quotation = str_repeat('<blockquote>', $q - $quote_level);
717        else if ($q < $quote_level)
718          $quotation = str_repeat("</blockquote>", $quote_level - $q);
719        }
720      else if ($quote_level > 0)
721        $quotation = str_repeat("</blockquote>", $quote_level);
722
723      $quote_level = $q;
724      $a_lines[$n] = $quotation . Q($line, 'replace', FALSE);
725      }
726
727    // insert the links for urls and mailtos
728    $body = preg_replace("/##string_replacement\{([0-9]+)\}##/e", "\$replace_strings[\\1]", join("\n", $a_lines));
729   
730    return "<div class=\"pre\">".$body."\n</div>";
731    }
732  }
733
734
735
736// add a string to the replacement array and return a replacement string
737function rcmail_str_replacement($str, &$rep)
738  {
739  static $count = 0;
740  $rep[$count] = stripslashes($str);
741  return "##string_replacement{".($count++)."}##";
742  }
743
744
745function rcmail_parse_message(&$structure, $arg=array(), $recursive=FALSE)
746  {
747  global $IMAP;
748  static $sa_inline_objects = array();
749
750  // arguments are: (bool)$prefer_html, (string)$get_url
751  extract($arg);
752
753  $a_attachments = array();
754  $a_return_parts = array();
755  $out = '';
756
757  $message_ctype_primary = strtolower($structure->ctype_primary);
758  $message_ctype_secondary = strtolower($structure->ctype_secondary);
759
760  // show message headers
761  if ($recursive && is_array($structure->headers) && isset($structure->headers['subject']))
762    {
763    $c = new stdClass;
764    $c->type = 'headers';
765    $c->headers = &$structure->headers;
766    $a_return_parts[] = $c;
767    }
768
769  // print body if message doesn't have multiple parts
770  if ($message_ctype_primary=='text')
771    {
772    $structure->type = 'content';
773    $a_return_parts[] = &$structure;
774    }
775   
776  // message contains alternative parts
777  else if ($message_ctype_primary=='multipart' && $message_ctype_secondary=='alternative' && is_array($structure->parts))
778    {
779    // get html/plaintext parts
780    $plain_part = $html_part = $print_part = $related_part = NULL;
781   
782    foreach ($structure->parts as $p => $sub_part)
783      {
784      $rel_parts = $attachmnts = null;
785      $sub_ctype_primary = strtolower($sub_part->ctype_primary);
786      $sub_ctype_secondary = strtolower($sub_part->ctype_secondary);
787
788      // check if sub part is
789      if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='plain')
790        $plain_part = $p;
791      else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='html')
792        $html_part = $p;
793      else if ($sub_ctype_primary=='text' && $sub_ctype_secondary=='enriched')
794        $enriched_part = $p;
795      else if ($sub_ctype_primary=='multipart' && ($sub_ctype_secondary=='related' || $sub_ctype_secondary=='mixed'))
796        $related_part = $p;
797      }
798     
799    // parse related part (alternative part could be in here)
800    if ($related_part!==NULL)
801    {
802      list($rel_parts, $attachmnts) = rcmail_parse_message($structure->parts[$related_part], $arg, TRUE);
803      $a_attachments = array_merge($a_attachments, $attachmnts);
804    }
805   
806    // merge related parts if any
807    if ($rel_parts && $prefer_html && !$html_part)
808      $a_return_parts = array_merge($a_return_parts, $rel_parts);
809
810    // choose html/plain part to print
811    else if ($html_part!==NULL && $prefer_html)
812      $print_part = &$structure->parts[$html_part];
813    else if ($enriched_part!==NULL)
814      $print_part = &$structure->parts[$enriched_part];
815    else if ($plain_part!==NULL)
816      $print_part = &$structure->parts[$plain_part];
817
818    // show message body
819    if (is_object($print_part))
820      {
821      $print_part->type = 'content';
822      $a_return_parts[] = $print_part;
823      }
824    // show plaintext warning
825    else if ($html_part!==NULL && empty($a_return_parts))
826      {
827      $c = new stdClass;
828      $c->type = 'content';
829      $c->body = rcube_label('htmlmessage');
830      $c->ctype_primary = 'text';
831      $c->ctype_secondary = 'plain';
832     
833      $a_return_parts[] = $c;
834      }
835                               
836    // add html part as attachment
837    if ($html_part!==NULL && $structure->parts[$html_part]!==$print_part)
838      {
839      $html_part = &$structure->parts[$html_part];
840      $html_part->filename = rcube_label('htmlmessage');
841      $html_part->mimetype = 'text/html';
842     
843      $a_attachments[] = $html_part;
844      }
845    }
846
847  // message contains multiple parts
848  else if (is_array($structure->parts) && !empty($structure->parts))
849    {
850    for ($i=0; $i<count($structure->parts); $i++)
851      {
852      $mail_part = &$structure->parts[$i];
853      $primary_type = strtolower($mail_part->ctype_primary);
854      $secondary_type = strtolower($mail_part->ctype_secondary);
855
856      // multipart/alternative
857      if ($primary_type=='multipart')
858        {
859        list($parts, $attachmnts) = rcmail_parse_message($mail_part, $arg, TRUE);
860
861        $a_return_parts = array_merge($a_return_parts, $parts);
862        $a_attachments = array_merge($a_attachments, $attachmnts);
863        }
864
865      // part text/[plain|html] OR message/delivery-status
866      else if (($primary_type=='text' && ($secondary_type=='plain' || $secondary_type=='html') && $mail_part->disposition!='attachment') ||
867               ($primary_type=='message' && ($secondary_type=='delivery-status' || $secondary_type=='disposition-notification')))
868        {
869        $mail_part->type = 'content';
870        $a_return_parts[] = $mail_part;
871        }
872
873      // part message/*
874      else if ($primary_type=='message')
875        {
876        list($parts, $attachmnts) = rcmail_parse_message($mail_part, $arg, TRUE);
877         
878        $a_return_parts = array_merge($a_return_parts, $parts);
879        $a_attachments = array_merge($a_attachments, $attachmnts);
880        }
881       
882      // ignore "virtual" protocol parts
883      else if ($primary_type=='protocol')
884        continue;
885
886      // part is file/attachment
887      else if ($mail_part->disposition=='attachment' || $mail_part->disposition=='inline' || $mail_part->headers['content-id'] ||
888               (empty($mail_part->disposition) && $mail_part->filename))
889        {
890        // skip apple resource forks
891        if ($message_ctype_secondary=='appledouble' && $secondary_type=='applefile')
892          continue;
893
894        // part belongs to a related message
895        if ($message_ctype_secondary=='related' && $mail_part->headers['content-id'])
896          {
897          $mail_part->content_id = preg_replace(array('/^</', '/>$/'), '', $mail_part->headers['content-id']);
898          $sa_inline_objects[] = $mail_part;
899          }
900        // is regular attachment
901        else
902          {
903          if (!$mail_part->filename)
904            $mail_part->filename = 'Part '.$mail_part->mime_id;
905          $a_attachments[] = $mail_part;
906          }
907        }
908      }
909
910    // if this was a related part try to resolve references
911    if ($message_ctype_secondary=='related' && sizeof($sa_inline_objects))
912      {
913      $a_replaces = array();
914       
915      foreach ($sa_inline_objects as $inline_object)
916        $a_replaces['cid:'.$inline_object->content_id] = htmlspecialchars(sprintf($get_url, $inline_object->mime_id));
917     
918      // add replace array to each content part
919      // (will be applied later when part body is available)
920      for ($i=0; $i<count($a_return_parts); $i++)
921        {
922        if ($a_return_parts[$i]->type=='content')
923          $a_return_parts[$i]->replaces = $a_replaces;
924        }
925      }
926    }
927
928  // message is single part non-text
929  else if ($structure->filename)
930    $a_attachments[] = $structure;
931
932  return array($a_return_parts, $a_attachments);
933  }
934
935
936
937
938// return table with message headers
939function rcmail_message_headers($attrib, $headers=NULL)
940  {
941  global $IMAP, $OUTPUT, $MESSAGE;
942  static $sa_attrib;
943 
944  // keep header table attrib
945  if (is_array($attrib) && !$sa_attrib)
946    $sa_attrib = $attrib;
947  else if (!is_array($attrib) && is_array($sa_attrib))
948    $attrib = $sa_attrib;
949 
950 
951  if (!isset($MESSAGE))
952    return FALSE;
953
954  // get associative array of headers object
955  if (!$headers)
956    $headers = is_object($MESSAGE['headers']) ? get_object_vars($MESSAGE['headers']) : $MESSAGE['headers'];
957 
958  $header_count = 0;
959 
960  // allow the following attributes to be added to the <table> tag
961  $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
962  $out = '<table' . $attrib_str . ">\n";
963
964  // show these headers
965  $standard_headers = array('subject', 'from', 'organization', 'to', 'cc', 'bcc', 'reply-to', 'date');
966 
967  foreach ($standard_headers as $hkey)
968    {
969    if (!$headers[$hkey])
970      continue;
971
972    if ($hkey=='date' && !empty($headers[$hkey]))
973      $header_value = format_date($headers[$hkey]);
974    else if (in_array($hkey, array('from', 'to', 'cc', 'bcc', 'reply-to')))
975      $header_value = Q(rcmail_address_string($headers[$hkey], NULL, $attrib['addicon']), 'show');
976    else
977      $header_value = Q(rcube_imap::decode_mime_string($headers[$hkey], $headers['charset']));
978
979    $out .= "\n<tr>\n";
980    $out .= '<td class="header-title">'.Q(rcube_label($hkey)).":&nbsp;</td>\n";
981    $out .= '<td class="'.$hkey.'" width="90%">'.$header_value."</td>\n</tr>";
982    $header_count++;
983    }
984
985  $out .= "\n</table>\n\n";
986
987  return $header_count ? $out : ''; 
988  }
989
990
991
992function rcmail_message_body($attrib)
993  {
994  global $CONFIG, $OUTPUT, $MESSAGE, $IMAP, $GET_URL, $REMOTE_OBJECTS;
995 
996  if (!is_array($MESSAGE['parts']) && !$MESSAGE['body'])
997    return '';
998   
999  if (!$attrib['id'])
1000    $attrib['id'] = 'rcmailMsgBody';
1001
1002  $safe_mode = $MESSAGE['is_safe'] || intval($_GET['_safe']);
1003  $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id'));
1004  $out = '<div '. $attrib_str . ">\n";
1005 
1006  $header_attrib = array();
1007  foreach ($attrib as $attr => $value)
1008    if (preg_match('/^headertable([a-z]+)$/i', $attr, $regs))
1009      $header_attrib[$regs[1]] = $value;
1010
1011
1012  // this is an ecrypted message
1013  // -> create a plaintext body with the according message
1014  if (!sizeof($MESSAGE['parts']) && $MESSAGE['headers']->ctype=='multipart/encrypted')
1015    {
1016    $p = new stdClass;
1017    $p->type = 'content';
1018    $p->ctype_primary = 'text';
1019    $p->ctype_secondary = 'plain';
1020    $p->body = rcube_label('encryptedmessage');
1021    $MESSAGE['parts'][0] = $p;
1022    }
1023 
1024  if ($MESSAGE['parts'])
1025    {
1026    foreach ($MESSAGE['parts'] as $i => $part)
1027      {
1028      if ($part->type=='headers')
1029        $out .= rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : NULL, $part->headers);
1030      else if ($part->type=='content')
1031        {
1032        if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset']))
1033          $part->ctype_parameters['charset'] = $MESSAGE['headers']->charset;
1034
1035        // fetch part if not available
1036        if (!isset($part->body))
1037          $part->body = $IMAP->get_message_part($MESSAGE['UID'], $part->mime_id, $part);
1038
1039        $body = rcmail_print_body($part, $safe_mode, !$CONFIG['prefer_html']);
1040        $out .= '<div class="message-part">';
1041       
1042        if ($part->ctype_secondary != 'plain')
1043          $out .= rcmail_sanitize_html($body, $attrib['id']);
1044        else
1045          $out .= $body;
1046
1047        $out .= "</div>\n";
1048        }
1049      }
1050    }
1051  else
1052    $out .= $MESSAGE['body'];
1053
1054
1055  $ctype_primary = strtolower($MESSAGE['structure']->ctype_primary);
1056  $ctype_secondary = strtolower($MESSAGE['structure']->ctype_secondary);
1057 
1058  // list images after mail body
1059  if (get_boolean($attrib['showimages']) && $ctype_primary=='multipart' &&
1060      !empty($MESSAGE['attachments']) && !strstr($message_body, '<html') && strlen($GET_URL))
1061    {
1062    foreach ($MESSAGE['attachments'] as $attach_prop)
1063      {
1064      if (strpos($attach_prop->mimetype, 'image/')===0)
1065        $out .= sprintf("\n<hr />\n<p align=\"center\"><img src=\"%s&amp;_part=%s\" alt=\"%s\" title=\"%s\" /></p>\n",
1066                        htmlspecialchars($GET_URL), $attach_prop->mime_id,
1067                        $attach_prop->filename,
1068                        $attach_prop->filename);
1069      }
1070    }
1071 
1072  // tell client that there are blocked remote objects
1073  if ($REMOTE_OBJECTS && !$safe_mode)
1074    $OUTPUT->set_env('blockedobjects', true);
1075
1076  $out .= "\n</div>";
1077  return $out;
1078  }
1079
1080
1081
1082// modify a HTML message that it can be displayed inside a HTML page
1083function rcmail_sanitize_html($body, $container_id)
1084  {
1085  // remove any null-byte characters before parsing
1086  $body = preg_replace('/\x00/', '', $body);
1087 
1088  $base_url = "";
1089  $last_style_pos = 0;
1090  $body_lc = strtolower($body);
1091 
1092  // check for <base href>
1093  if (preg_match(($base_reg = '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i'), $body, $base_regs))
1094    $base_url = $base_regs[2];
1095 
1096  // find STYLE tags
1097  while (($pos = strpos($body_lc, '<style', $last_style_pos)) && ($pos2 = strpos($body_lc, '</style>', $pos)))
1098    {
1099    $pos = strpos($body_lc, '>', $pos)+1;
1100
1101    // replace all css definitions with #container [def]
1102    $styles = rcmail_mod_css_styles(substr($body, $pos, $pos2-$pos), $container_id, $base_url);
1103
1104    $body = substr($body, 0, $pos) . $styles . substr($body, $pos2);
1105    $body_lc = strtolower($body);
1106    $last_style_pos = $pos2;
1107    }
1108
1109
1110  // remove SCRIPT tags
1111  foreach (array('script', 'applet', 'object', 'embed', 'iframe') as $tag)
1112    {
1113    while (($pos = strpos($body_lc, '<'.$tag)) && (($pos2 = strpos($body_lc, '</'.$tag.'>', $pos)) || ($pos3 = strpos($body_lc, '>', $pos))))
1114      {
1115      $end = $pos2 ? $pos2 + strlen('</'.$tag.'>') : $pos3 + 1;
1116      $body = substr($body, 0, $pos) . substr($body, $end, strlen($body)-$end);
1117      $body_lc = strtolower($body);
1118      }
1119    }
1120
1121  // replace event handlers on any object
1122  while ($body != $prev_body)
1123    {
1124    $prev_body = $body;
1125    $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);
1126    $body = preg_replace('/(<[^!][^>]*\shref=["\']?)(javascript:)([^>]*?>)/im', '$1null:$3', $body);
1127    }
1128
1129  // resolve <base href>
1130  if ($base_url)
1131    {
1132    $body = preg_replace('/(src|background|href)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Uie', "'\\1=\"'.make_absolute_url('\\3', '$base_url').'\"'", $body);
1133    $body = preg_replace('/(url\s*\()(["\']?)([\.\/]+[^"\'\)\s]+)(\2)\)/Uie', "'\\1\''.make_absolute_url('\\3', '$base_url').'\')'", $body);
1134    $body = preg_replace($base_reg, '', $body);
1135    }
1136   
1137  // modify HTML links to open a new window if clicked
1138  $body = preg_replace('/<(a|link)\s+([^>]+)>/Uie', "rcmail_alter_html_link('\\1','\\2', '$container_id');", $body);
1139
1140  // add comments arround html and other tags
1141  $out = preg_replace(array(
1142      '/(<!DOCTYPE[^>]*>)/i',
1143      '/(<\?xml[^>]*>)/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 rcube_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($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' => array($OUTPUT, 'search_form'),
1545));
1546
1547?>
Note: See TracBrowser for help on using the repository browser.